package cn.yy.logreportmanager.service.impl;

import cn.yy.logreportmanager.dto.common.ErrorRespBody;
import cn.yy.logreportmanager.dto.common.Response;
import cn.yy.logreportmanager.dto.common.ResponseBean;
import cn.yy.logreportmanager.dto.handover.CusHandoverHisBody;
import cn.yy.logreportmanager.dto.handover.CusHandoverHisEntry;
import cn.yy.logreportmanager.entity.Customer;
import cn.yy.logreportmanager.entity.Department;
import cn.yy.logreportmanager.entity.Employee;
import cn.yy.logreportmanager.entity.Handoverlog;
import cn.yy.logreportmanager.entity.Opportunity;
import cn.yy.logreportmanager.mapper.CustomerMapper;
import cn.yy.logreportmanager.mapper.DepartmentMapper;
import cn.yy.logreportmanager.mapper.EmployeeMapper;
import cn.yy.logreportmanager.mapper.HandoverlogMapper;
import cn.yy.logreportmanager.mapper.OpportunityMapper;
import cn.yy.logreportmanager.service.HandoverService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class HandoverServiceImpl implements HandoverService {
    @Autowired
    EmployeeMapper employeeMapper;
    @Autowired
    DepartmentMapper departmentMapper;
    @Autowired
    CustomerMapper customerMapper;
    @Autowired
    OpportunityMapper opportunityMapper;
    @Autowired
    HandoverlogMapper handoverlogMapper;

    private static class CondMapKey{
        public static final String inDeptID = "inDeptID";
        public static final String inCusMgrID = "inCusMgrID";
        public static final String outDeptID = "outDeptID";
        public static final String outCusMgrID = "outCusMgrID";
        public static final String cusName = "cusName";
        public static final String oppName = "oppName";
    }
    @Override
    public ResponseBean<?> conditionQuery(Map<String, String> conMap) {
        ResponseBean<ErrorRespBody> checkResp = condQueryArgCheck(conMap);
        if (checkResp != null) return checkResp;

        LambdaQueryWrapper<Handoverlog> queryWrapper = new LambdaQueryWrapper<>();
        List<String> inCusMgrIDs = new ArrayList<>();
        List<String> outCusMgrIDs = new ArrayList<>();
        inCusMgrIDs = getMgrIDStrings(conMap, inCusMgrIDs, CondMapKey.inCusMgrID, CondMapKey.inDeptID);
        outCusMgrIDs = getMgrIDStrings(conMap, outCusMgrIDs, CondMapKey.outCusMgrID, CondMapKey.outDeptID);

        if (tryUpdateQWWithIDs(queryWrapper, inCusMgrIDs, Handoverlog::getHInCusManagerId))
            return wrapHisListAsResponse(new ArrayList<>());
        if (tryUpdateQWWithIDs(queryWrapper, outCusMgrIDs, Handoverlog::getHOutCusManagerId))
            return wrapHisListAsResponse(new ArrayList<>());

        if (checkCusNOppo(conMap, queryWrapper)) return wrapHisListAsResponse(new ArrayList<>());

        return wrapHisListAsResponse(
                handoverlogMapper.selectList(queryWrapper)
        );
    }

    private boolean checkCusNOppo(Map<String, String> conMap, LambdaQueryWrapper<Handoverlog> queryWrapper) {
        List<String> cusIDs = null;
        if (conMap.containsKey(CondMapKey.cusName)){
            cusIDs = customerMapper.selectList(
                    new LambdaQueryWrapper<Customer>()
                            .like(Customer::getCusName, "%"+ conMap.get(CondMapKey.cusName)+"%")
            ).stream().map(Customer::getCusId).collect(Collectors.toList());
        }
        if (tryUpdateQWWithIDs(queryWrapper, cusIDs, Handoverlog::getHCusId))
            return true;

        List<String> oppIDs = null;
        if (conMap.containsKey(CondMapKey.oppName)){
            oppIDs = opportunityMapper.selectList(
                    new LambdaQueryWrapper<Opportunity>()
                            .like(Opportunity::getOppName, "%"+ conMap.get(CondMapKey.oppName)+"%")
            ).stream().map(Opportunity::getOppId).collect(Collectors.toList());
        }
        return tryUpdateQWWithIDs(queryWrapper, oppIDs, Handoverlog::getHOppId);
    }

    private boolean tryUpdateQWWithIDs(LambdaQueryWrapper<Handoverlog> queryWrapper, List<String> IDs,
                                       SFunction<Handoverlog,?> lambda) {
        if (IDs != null){
            if (IDs.size() == 0){
                return true;
            }
            queryWrapper.and(qw -> {
                for (int i = 0; i< IDs.size(); i++){
                    if (i != 0) qw.or();
                    qw.eq(lambda, IDs.get(i));
                }
            });
        }
        return false;
    }

    private List<String> getMgrIDStrings(Map<String, String> conMap, List<String> cusMgrIDs,
                                         String cusMgrID, String deptID) {
        if (conMap.containsKey(cusMgrID)) {
            cusMgrIDs.add(cusMgrID);
        } else if (conMap.containsKey(deptID)) {
            cusMgrIDs.addAll(
                    employeeMapper.selectList(
                            new LambdaQueryWrapper<Employee>()
                                    .eq(Employee::getEmpDeptId, conMap.get(deptID))
                    ).stream().map(Employee::getEmpId).collect(Collectors.toList())
            );
        } else {
            cusMgrIDs = null;
        }
        return cusMgrIDs;
    }

    private ResponseBean<ErrorRespBody> condQueryArgCheck(Map<String, String> conMap) {
        Set<String> keySet = new HashSet<>(conMap.keySet());
        for (String key:keySet){
            if (conMap.get(key) == null){
                conMap.remove(key);
            }
        }
        if (conMap.containsKey(CondMapKey.inCusMgrID) && conMap.containsKey(CondMapKey.inDeptID)){
            Employee employee = employeeMapper.selectById(conMap.get(CondMapKey.inCusMgrID));
            if (employee == null){
                return Response.simpleErr("Employee ID Not Found: " + conMap.get(CondMapKey.inCusMgrID),"");
            }
            Department department = departmentMapper.selectById(employee.getEmpDeptId());
            if (department == null) {
                return Response.simpleErr(
                        "Employee ("+ conMap.get(CondMapKey.inCusMgrID)+") Not Found " +
                                "in Department ("+ conMap.get(CondMapKey.inDeptID)+")","");
            }
        }
        if (conMap.containsKey(CondMapKey.outCusMgrID) && conMap.containsKey(CondMapKey.outDeptID)){
            Employee employee = employeeMapper.selectById(conMap.get(CondMapKey.outCusMgrID));
            if (employee == null){
                return Response.simpleErr("Employee ID Not Found: " + conMap.get(CondMapKey.outCusMgrID),"");
            }
            Department department = departmentMapper.selectById(employee.getEmpDeptId());
            if (department == null) {
                return Response.simpleErr(
                        "Employee ("+ conMap.get(CondMapKey.outCusMgrID)+") Not Found " +
                                "in Department ("+ conMap.get(CondMapKey.outDeptID)+")","");
            }
        }
        return null;
    }

    private ResponseBean<?> wrapHisListAsResponse(List<Handoverlog> list){
        List<CusHandoverHisEntry> entries = list.stream().map(handoverLog -> {
            CusHandoverHisEntry entry = new CusHandoverHisEntry();
            Customer customer = customerMapper.selectById(handoverLog.getHCusId());
            Opportunity opportunity = opportunityMapper.selectById(handoverLog.getHOppId());
            Employee inMgr = employeeMapper.selectById(handoverLog.getHInCusManagerId());
            Department inDept = departmentMapper.selectById(inMgr.getEmpDeptId());
            Employee outMgr = employeeMapper.selectById(handoverLog.getHOutCusManagerId());
            Department outDept = departmentMapper.selectById(outMgr.getEmpDeptId());

            entry.setCusID(handoverLog.getHCusId());
            entry.setCusName(customer.getCusName());
            entry.setOppoID(handoverLog.getHOppId());
            entry.setOppoName(opportunity.getOppName());
            entry.setInCusMgrName(inMgr.getEmpName());
            entry.setOutCusMgrName(outMgr.getEmpName());
            entry.setInDeptName(inDept.getDeptName());
            entry.setOutDeptName(outDept.getDeptName());

            return entry;
        }).collect(Collectors.toList());
        CusHandoverHisBody body = new CusHandoverHisBody();
        body.setEntries(entries);
        ResponseBean<CusHandoverHisBody> responseBean = new ResponseBean<>();
        responseBean.setStatus(Response.STATE_OK);
        responseBean.setBody(body);
        return responseBean;
    }
}
